package com.orion.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.JdbcApprovalStore;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;

import javax.sql.DataSource;
import java.util.Collections;

/**
 * @author Administrator
 * @date 2021/9/24
 */


//@Configuration
//@EnableAuthorizationServer
public class Oauth2JdbcAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    public static final Integer ACCESS_TOKEN_VALID_SECONDS = 7200;
    public static final Integer REFRESH_TOKEN_VALID_SECONDS = 3 * 3600 * 3600;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private CustomBasicTokenFilter customBasicTokenFilter;

    @Autowired
    private DataSource dataSource;

    @Autowired
    private PasswordEncoder passwordEncoder;


    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.passwordEncoder(passwordEncoder);
        // 开启/oauth/check_token验证端口认证权限访问
        security.checkTokenAccess("isAuthenticated()");
        // 开启/oauth/token_key验证端口无权限访问
        security.tokenKeyAccess("permitAll()");
        /*
         *
         * 主要是让/oauth/token支持client_id和client_secret做登陆认证
         * 如果开启了allowFormAuthenticationForClients，那么就在BasicAuthenticationFilter之前
         * 添加ClientCredentialsTokenEndpointFilter,使用ClientDetailsUserDetailsService来进行登陆认证
         *
         * 前后分离返回统一json不开启
         */
        //security.allowFormAuthenticationForClients();
        security.addTokenEndpointAuthenticationFilter(customBasicTokenFilter);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(jdbcClientDetailsService());
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //MUST：密码模式下需设置一个AuthenticationManager对象,获取 UserDetails信息
        //末确认点.userDetailsService(userDetailsService)

        /*
          使用 /pig4cloud/login 覆盖 原有的/oauth/token，注意这里是覆盖一旦配置 原有路径将失效
          endpoints.pathMapping("/oauth/token","/pig4cloud/login");
        */

        endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
        endpoints.approvalStore(jdbcApprovalStore());
        endpoints.authorizationCodeServices(jdbcAuthorizationCodeServices());
        /*
          这个是处理oauth2那些grant_type、invalid code 之类的异常返回，但是clientId、clientSecret这些错它不管的，
          要靠上面上面的security来配置security.authenticationEntryPoint()
         */
        endpoints.exceptionTranslator(new CustomWebResponseExceptionTranslator());
         /* 有了tokenServices就不设置了
        endpoints.authenticationManager(authenticationManager);

        endpoints.setClientDetailsService(jdbcClientDetailsService());
        endpoints.tokenStore(new JdbcTokenStore(dataSource));


        //token增强器，多加点信息在里面
        endpoints.tokenEnhancer(tokenEnhancerChain());
        */

        //tokenServices没有的话会自动创建一个默认的
        endpoints.tokenServices(customTokenService());
        //密码password模式必须加上
        endpoints.authenticationManager(authenticationManager);
    }

    @Bean
    public DefaultTokenServices customTokenService() {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setSupportRefreshToken(true);
        tokenServices.setAccessTokenValiditySeconds(3 * 3600);
        tokenServices.setRefreshTokenValiditySeconds(3 * 3 * 3600);
        tokenServices.setReuseRefreshToken(true);
        tokenServices.setClientDetailsService(jdbcClientDetailsService());
        tokenServices.setTokenEnhancer(tokenEnhancerChain());
        tokenServices.setTokenStore(new JdbcTokenStore(dataSource));
        tokenServices.setAuthenticationManager(authenticationManager);
        return tokenServices;
    }

    @Bean
    public TokenEnhancerChain tokenEnhancerChain() {
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Collections.singletonList(new CustomTokenEnhancer()));
        return tokenEnhancerChain;
    }

    @Bean
    public AuthorizationCodeServices jdbcAuthorizationCodeServices() {
        return new JdbcAuthorizationCodeServices(dataSource);
    }

    @Bean
    public ApprovalStore jdbcApprovalStore() {
        return new JdbcApprovalStore(dataSource);
    }

    @Bean
    public ClientDetailsService jdbcClientDetailsService() {
        //从数据库中读取client_id,client_secret
        return new JdbcClientDetailsService(dataSource);
    }
}
